In [ ]:
#@title Imports
import scipy.io as sio
'''
This statement imports the scipy.io module and assigns it an alias sio.
 The scipy.io module provides functions for working with various
 file formats used in scientific computing, such as MATLAB files.
'''
import scipy.misc as spm
''' This statement imports the scipy.misc module and assigns it an alias spm.
 The scipy.misc module provides various utility functions for image manipulation
  and processing, such as reading and saving images.
'''
from scipy import ndimage
'''
 This statement imports the ndimage module from the scipy package.
  The ndimage module provides functions for multi-dimensional image processing, 
 such as filtering, interpolation, and morphology operations.
'''
import cv2 as cv
import os
import numpy as np
import matplotlib.image as plt
from IPython.display import Image, display
%matplotlib inline
import pandas as pd
from scipy import stats, integrate
import seaborn as sns
sns.set(color_codes=True)
from collections import Counter
import torch
from torch import nn
import matplotlib.pyplot as plt
import torch.nn.functional as F
import torchvision
from torch.utils.data import Dataset, DataLoader
from torch.autograd import Variable
from torch.utils.data.sampler import SubsetRandomSampler
import glob
import albumentations as A
from albumentations.core.composition import OneOf
from plotly import express as px

!pip install --upgrade plotly;
!pip install torchmetrics;
!pip install albumentations==0.4.6;
from torchmetrics import Accuracy, Precision, Recall
In [18]:
#@title All Dirs Paths { display-mode: "form",run: "auto" }

class CFG:
  IMG_DIR_IMDB = os.path.join('/content/imdb_crop') #@param {type:"string"}
  MAT_FILE_IMDB = os.path.join('/content/imdb_crop/imdb.mat')#@param {type:"string"}
  #@markdown inside wiki data there is no celeb_id so i cannot take from that the image we wanted
  IMG_DIR_WIKI = os.path.join('/content/wiki_crop')#@param {type:"string"}
  MAT_FILE_WIKI = os.path.join('/content/wiki_crop/wiki.mat')#@param {type:"string"}
In [136]:
#@title Functions 
def readData(num=0):
    if num == 0:
        b = pd.read_csv('/content/celebs4face_detection.csv')
    else:
        b = pd.read_csv('/content/faces_meta_data.csv')

    b.replace('', np.NaN, inplace=True)
    return b

def create_subplots(images:list, titles:list = None,wantAxis = False,figsize=(12, 8)):
    fig = plt.figure(figsize=figsize)
    num_subplots = len(images)
    for i in range(num_subplots):
        plt.subplot(1, num_subplots, i+1)
        plt.imshow(images[i], cmap="gray")
        plt.axis("off" if wantAxis ==False else "on")
        plt.title(titles[i] if titles != None else i)
    plt.show()



def show(image,WantAxis=True,text="Image"):
  create_subplots([image],[text],WantAxis,(6,4))


def makeFromDictPandas(theDict):
  return pd.DataFrame(theDict)
  
def makeDFtoCSV(theDF,name):
  theString = str(name)+'.csv'
  theDF.to_csv(theString, index=False)

def unpack_path(path):
    return os.path.join(CFG.IMG_DIR_IMDB, path[0])

def unpack_name(name):
    return name[0]

def getDataReady():
  mat_struct = sio.loadmat(CFG.MAT_FILE_IMDB)
  data_set = [data[0] for data in mat_struct['imdb'][0, 0]]

  keys = ['dob',
    'photo_taken',
    'full_path',
    'gender',
    'name',
    'face_location',
    'face_score',
    'second_face_score',
    'celeb_names',
    'celeb_id']
  imdb_dict = dict(zip(keys, np.asarray(data_set)))

  del imdb_dict['dob']
  del imdb_dict['celeb_names']
  imdb_dict['full_path'] = [unpack_path(path) for path in imdb_dict['full_path']]
  imdb_dict['name'] = [unpack_name(name) for name in imdb_dict['name']]
  nf = makeFromDictPandas(imdb_dict)
  nf.replace('', np.NaN, inplace=True)
  nf = nf[~np.isinf(nf['face_score'])]
  nf = nf[nf['face_score'].notna()]
  nf = nf[nf['second_face_score'].isna()]
  nf = nf.drop(['second_face_score'], axis=1)
  tf = readData(0)
  arr = tf['celeb_id'].tolist()
  nf = nf[nf['celeb_id'].isin(arr)]
  return nf

First, the labels, which was not easily obtained. The meta data is stored separately and in a .mat file. (Yes, matlab)!

Luckily we can use the scipy.io.loadmat to load the .mat file to python accessible (kind of) format. We can access the dob by some proper indexing.

In [76]:
#@title clean directory { run: "auto" }
import shutil
def cleanDirectorys(path):
  if os.path.exists(f'{path}'):
    shutil.rmtree(f'{path}')
#@markdown enter which directory to clean from files
theDir = '' #@param {type:"string"}
cleanDirectorys(theDir)
In [ ]:
#@title Install the data ? { run: "auto" }
def installData():
  !wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/wiki_crop.tar
  !tar -xf wiki_crop.tar
  !wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/imdb_crop.tar
  !tar -xf imdb_crop.tar
install_Data = False #@param {type:"boolean"}
if install_Data:
  installData()
In [91]:
#@title Get ready the data ? { run: "auto" }
ok = True #@param {type:'boolean'}
if ok:
  nf = getDataReady();
<ipython-input-56-bce360e20f91>:42: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  imdb_dict = dict(zip(keys, np.asarray(data_set)))
/usr/local/lib/python3.10/dist-packages/pandas/core/missing.py:95: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  new_mask = arr == x
In [ ]:
#@title The data is clean and ready.
nf.info();
In [ ]:
#@title Print images
all_ids = list(set(nf['celeb_id']))
num_images = 5
fig, axs = plt.subplots(len(all_ids), num_images, figsize=(36, 22))
sum_images_for_swapping = []

for i, value in enumerate(all_ids):
    full_paths = nf.loc[nf['celeb_id'] == value, 'full_path'].tail(num_images)
    names = nf.loc[nf['celeb_id'] == value, 'name'].tail(num_images)  # Retrieve corresponding names
    
    for j, (path, name) in enumerate(zip(full_paths, names)):
        image = plt.imread(path)
        axs[i, j].imshow(image, cmap="gray")
        axs[i, j].axis("off")
        axs[i, j].set_title(f"Name: {name}")  # Set the title as the name
        
        sum_images_for_swapping.append(image)  

plt.tight_layout()
plt.show()
In [111]:
#@title FaceDataset
class FaceDataset(Dataset):
  def __init__(self,img_paths,label ,transforms = None):
    self.img_paths = img_paths
    self.label = label
    self.transform = transforms

  def __getitem__(self, idx):
    image_path = self.img_paths[idx]#gets the image in place idx from the diractory
    image_label = self.label[idx]
    img = cv.imread(image_path)
    img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
    img = cv.resize(img, (220,220), interpolation = cv.INTER_AREA)#so all images be the same size

    if self.transform:
      img = self.transform(image = img)["image"]
      
    return img , image_label

  def __len__(self):
    return len(self.img_paths)

  def printImage(self,idx):
    image_path = self.img_paths[idx]
    image_label = self.label[idx]
    img = cv.imread(image_path)
    img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
    img = cv.resize(img, (220,220), interpolation = cv.INTER_AREA)
    show(image_path,WantAxis=False,text=image_label)
In [ ]:
#@title # Augmentation
transform = A.Compose([
    OneOf([A.MedianBlur(blur_limit=9), A.Blur(blur_limit=8), A.GlassBlur(sigma=0.2, max_delta=2, p=0.4)],p=0.5),
    A.GaussNoise (var_limit=(50.0, 150.0), mean=20, p=0.6),
    A.Rotate(limit=45),
    A.HorizontalFlip(p=0.5),
    A.CLAHE (clip_limit=4.0, tile_grid_size=(8, 8), p=0.5),
    A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.3,p=0.3),
])

Gauss_Noise :

Adding Gaussian noise to the images can help make the face detection model more robust to real-world noise present in various environments. It introduces variations in pixel intensity, which can improve the model's ability to handle noisy input and enhance its generalization capability.

HorizontalFlip :

Flipping the images horizontally creates mirror images, effectively doubling the amount of training data. It helps the model learn to detect faces from different viewpoints and orientations, leading to improved accuracy and robustness.

RandomBrightnessContrast :

Randomly adjusting the brightness and contrast of the images adds diversity to the training data. This augmentation technique helps the model learn to detect faces under different lighting conditions, making it more adaptable to varying environments.

CLAHE (Contrast Limited Adaptive Histogram Equalization):

Applying CLAHE enhances the contrast of local regions within the image. It can improve the visibility of subtle facial details and features, enabling the model to better identify and detect faces.

MedianBlur:

Median blur is effective in reducing noise while preserving edges and fine details. This augmentation technique can help remove noise or artifacts from the images, making the face detection process more accurate and reliable.

In [158]:
#@title # Augmentations example

all_paths = nf['full_path'].values
all_names = nf['name'].values
img = plt.imread(all_paths[150])
label = all_names[150]

def showAug(img,name,transform_example):
  example_img = transform_example(image = img)["image"]
  show(example_img,WantAxis=False,text=name)

transform = A.Compose([A.Compose([A.GaussNoise (var_limit=(50.0, 150.0), mean=20, always_apply=True)])
,A.Compose([A.HorizontalFlip(always_apply=True)])
, A.Compose([A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.3, always_apply=True)])
,A.Compose([A.CLAHE(clip_limit=4.0, tile_grid_size=(8, 8), always_apply=True, p=0.5)]),
 A.Compose([A.MedianBlur(blur_limit=9, always_apply=True)])
])

Gauss_Noise = A.Compose([A.GaussNoise (var_limit=(50.0, 150.0), mean=20, always_apply=True)])

HorizontalFlip = A.Compose([A.HorizontalFlip(always_apply=True)])

RandomBrightnessContrast = A.Compose([A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.3, always_apply=True)])

CLAHE = A.Compose([A.CLAHE(clip_limit=4.0, tile_grid_size=(8, 8), always_apply=True, p=0.5)])

median_blur = A.Compose([A.MedianBlur(blur_limit=9, always_apply=True)])

arr_aug = [Gauss_Noise,HorizontalFlip,RandomBrightnessContrast,CLAHE,median_blur];
In [153]:
#@title Original image
show(img,WantAxis=False,text=label)
In [154]:
#@title Gauss_Noise
showAug(img,label,arr_aug[0])
In [155]:
#@title HorizontalFlip
showAug(img,label,arr_aug[1])
In [156]:
#@title RandomBrightnessContrast
showAug(img,label,arr_aug[2])
In [128]:
#@title CLAHE
showAug(img,label,arr_aug[3])
In [157]:
#@title median_blur
showAug(img,label,arr_aug[4])
In [160]:
#@title ## Create DataLoader and divide to train and validation
dataset = FaceDataset(all_paths,all_names, transform)#creat instanse of FaceDataset
BS = 16
#split to 80% train 20% validation
indices = list(range(len(dataset)))
split = int(np.floor(0.2 * len(dataset)))
np.random.seed(seed=42)
np.random.shuffle(indices)
train_indices, val_indices = indices[split:], indices[:split]

# Creating PT data samplers and loaders:
train_sampler = SubsetRandomSampler(train_indices);
valid_sampler = SubsetRandomSampler(val_indices);
#create data loaders for train and validation
train_loader = DataLoader(dataset, batch_size=BS, num_workers=4, sampler=train_sampler);
validation_loader = DataLoader(dataset, batch_size=BS, num_workers=4, sampler=valid_sampler);
In [163]:
#@title Ready to work ! :)
x,y = next(iter(train_loader))
x = x.cpu().numpy()
# y = y.cpu().numpy()
plt.figure(figsize=[17,15])
for i,(row,t) in enumerate(zip(x,y)):
    plt.subplot(4,4,i+1)
    plt.axis("off")
    plt.imshow(row)
    plt.title(t)
plt.show()
In [273]:
def getImages():
  img1 = cv.imread("/content/Screenshot_20230121_202601.jpg")
  img2 = cv.imread("/content/Screenshot_20180122-172101_wo_500_500.png")
  img2 = cv.resize(img2,(1080, 1253))
  return img1.copy(),img2.copy()
In [237]:
def switch_face(img1, img2, face1, face2):
    x1, y1, w1, h1 = face1
    x2, y2, w2, h2 = face2

    # Extract the face regions from the images
    face1_img = img1[y1:y1+h1, x1:x1+w1]
    face2_img = img2[y2:y2+h2, x2:x2+w2]

    # Resize the face regions to match each other's size
    face1_img_resized = cv.resize(face1_img, (w2, h2))
    face2_img_resized = cv.resize(face2_img, (w1, h1))

    # Swap the faces by replacing the corresponding regions in the images
    img1[y1:y1+h1, x1:x1+w1] = face2_img_resized
    img2[y2:y2+h2, x2:x2+w2] = face1_img_resized
    img1 = cv.cvtColor(img1,cv.COLOR_BGR2RGB)
    img2 = cv.cvtColor(img2,cv.COLOR_BGR2RGB)
    # Return the modified images
    return img1, img2
In [275]:
img1,img2 = getImages()
g = [img1,img2]
original = []
plt.figure(figsize=(20,9))
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml')
i=1
for augmanted_image in g:
    image = augmanted_image
    gray_image = cv.cvtColor(image,cv.COLOR_RGB2GRAY)
    frame_gray = cv.equalizeHist(gray_image)
    faces = face_cascade.detectMultiScale(frame_gray, 1.1, 4)
    for (x, y, w, h) in faces:
        cv.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
    normal = cv.cvtColor(image,cv.COLOR_BGR2RGB);
    show(normal,WantAxis=False)
    original.append(normal)
<Figure size 2000x900 with 0 Axes>
In [276]:
def detact_face(img, face_cascade):
  # Convert into grayscale
  gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
  # Detect faces
  faces = face_cascade.detectMultiScale(gray, 1.1, 4)
  return faces[0]
img1,img2 = getImages();
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml');
face1 = detact_face(img1, face_cascade);
face2 = detact_face(img2, face_cascade);
img1,img2=switch_face(img1, img2, face1, face2);
In [278]:
create_subplots([original[0],original[1],img1,img2],['Dori','Neta','DoriNeta','NetaDori']);
In [281]:
#@title TL;DR

'''
In this exercise, I made a custom dataset and data loader for face detection using the images my class uploaded to the drive.

I applied some augmentations I had seen fit the provided data to imitate scenarios that can occur in photos that are taken on a day-to-day basis, I also presented the augmentations examples on the data.

After doing so, I divided the data into 20% validation and 80% train and created the data loaders.

In the second part, I used OpenCV to detect faces in an image, I also created a function that swapped faces between two images.

''';
In [279]:
%%shell
jupyter nbconvert --to html
  File "<ipython-input-279-96ed913bdb4d>", line 2
    jupyter nbconvert --to html your_notebook.ipynb
            ^
SyntaxError: invalid syntax